home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 8352 / 8352.xpi / components / gfGreasefireService.js next >
Text File  |  2009-05-24  |  22KB  |  760 lines

  1. /*
  2.  * Copyright (C) 2008 by Steve Krulewitz <skrulx@gmail.com>
  3.  * Licensed under GPLv2 or later, see file LICENSE in the xpi for details.
  4.  */
  5. const Cc = Components.classes;
  6. const Ci = Components.interfaces;
  7. const Cr = Components.results;
  8.  
  9. Components.utils.import("resource://gre/modules/ISO8601DateUtils.jsm");
  10.  
  11. const DEBUG = false;
  12.  
  13. const NS_PROFILE_STARTUP_OBSERVER_ID  = "profile-after-change";
  14. const NS_PROFILE_SHUTDOWN_OBSERVER_ID = "profile-before-change";
  15.  
  16. function GF_GetIndexesDir() {
  17.   var em = Cc["@mozilla.org/extensions/manager;1"]
  18.              .getService(Ci.nsIExtensionManager);
  19.   var installLocation = em.getInstallLocation("greasefire@skrul.com");
  20.   var file = installLocation.location;
  21.   file.append("greasefire@skrul.com");
  22.   file.append("indexes");
  23.  
  24.   return file;
  25. }
  26.  
  27. function d(s) {
  28.   if (DEBUG) {
  29.     dump("gfGreasefireService: " + s + "\n");
  30.   }
  31. }
  32.  
  33. function gfGreasefireService()
  34. {
  35.   d("ctor");
  36.  
  37.   this._started = false;
  38.   this._includes = null;
  39.   this._excludes = null;
  40.   this._conn = null;
  41.  
  42.   this._scriptCount = null;
  43.   this._indexDate = Date.now();
  44.  
  45.   var obs = Cc["@mozilla.org/observer-service;1"]
  46.               .getService(Ci.nsIObserverService);
  47.   obs.addObserver(this, NS_PROFILE_STARTUP_OBSERVER_ID, false);
  48.   obs.addObserver(this, NS_PROFILE_SHUTDOWN_OBSERVER_ID, false);
  49. }
  50.  
  51. gfGreasefireService.prototype = {
  52.   classDescription: "Greasefire Service",
  53.   classID:          Components.ID("{d647ff9b-ac4c-4d0e-8fbd-484765be5549}"),
  54.   contractID:       "@skrul.com/greasefire/service;1"
  55. }
  56.  
  57. gfGreasefireService.prototype.startup =
  58. function gfGreasefirbeService_startup()
  59. {
  60.   d("startup");
  61.  
  62.   if (this._started) {
  63.     return;
  64.   }
  65.  
  66.   this._scriptCount = null;
  67.   this._indexDate = null;
  68.  
  69.   var dir = GF_GetIndexesDir();
  70.  
  71.   var file = dir.clone();
  72.   file.append("include.dat");
  73.   this._includes = new gfIndexReader(file);
  74.  
  75.   var file = dir.clone();
  76.   file.append("exclude.dat");
  77.   this._excludes = new gfIndexReader(file);
  78.  
  79.   var storageService = Cc["@mozilla.org/storage/service;1"]
  80.                          .getService(Ci.mozIStorageService);
  81.   file = dir.clone();
  82.   file.append("scripts.db");
  83.   this._conn = storageService.openDatabase(file);
  84.  
  85.   this._started = true;
  86. }
  87.  
  88. gfGreasefireService.prototype.shutdown =
  89. function gfGreasefireService_shutdown()
  90. {
  91.   d("shutdown");
  92.  
  93.   if (!this._started) {
  94.     return;
  95.   }
  96.  
  97.   if (this._includes) {
  98.     this._includes.close();
  99.   }
  100.  
  101.   if (this._excludes) {
  102.     this._excludes.close();
  103.   }
  104.  
  105.   // Only mozilla 1.9 has a close method
  106.   if (this._conn && this._conn.close) {
  107.     this._conn.close();
  108.   }
  109.  
  110.   this._started = false;
  111. }
  112.  
  113. // gfIGreasefireService
  114.  
  115. gfGreasefireService.prototype.hasScripts =
  116. function gfGreasefireService_hasScripts(aURL)
  117. {
  118.   var urlSpec = this._fixUrl(aURL);
  119.  
  120.   var excludes = {};
  121.   this._excludes.search(urlSpec, excludes, false);
  122.  
  123.   var matches = {};
  124.   this._includes.search(urlSpec, matches, true, excludes);
  125.  
  126.   for (var id in matches) {
  127.     return true;
  128.   }
  129.  
  130.   return false;
  131. }
  132.  
  133. gfGreasefireService.prototype.search =
  134. function gfGreasefireService_search(aURL)
  135. {
  136.   var urlSpec = this._fixUrl(aURL);
  137.  
  138.   var excludes = {};
  139.   var t = Date.now();
  140.   this._excludes.search(urlSpec, excludes, false);
  141.   var excludesMark = Date.now();
  142.  
  143.   var matches = {};
  144.   this._includes.search(urlSpec, matches, false, excludes);
  145.   var searchMark = Date.now();
  146.  
  147.   var infos = this._getScriptInfos(matches);
  148.   var ranks = this._rankMatches(matches, infos);
  149.  
  150.   var result = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
  151.   for (var id in matches) {
  152.     var r = new gfSearchResult(id, infos[id], matches[id], ranks[id]);
  153.     result.appendElement(r, false);
  154.   }
  155.  
  156.   d("****** Search " + aURL.spec +
  157.     " matches " + result.length +
  158.     " exclude " + (excludesMark - t) +
  159.     " include " + (searchMark - t));
  160.  
  161.   return result;
  162. }
  163.  
  164. gfGreasefireService.prototype.__defineGetter__("scriptCount",
  165. function gfGreasefireService_get_scriptCount()
  166. {
  167.   if (!this._started) {
  168.     throw Cr.NS_ERROR_UNEXPECTED;
  169.   }
  170.  
  171.   if (!this._scriptCount) {
  172.     var sql = "select count(1) from scripts";
  173.     var stmt = this._conn.createStatement(sql);
  174.     stmt.executeStep();
  175.     this._scriptCount = stmt.getInt32(0);
  176.     stmt.finalize();
  177.   }
  178.  
  179.   return this._scriptCount;
  180. });
  181.  
  182. gfGreasefireService.prototype.__defineGetter__("indexDate",
  183. function gfGreasefireService_get_indexDate()
  184. {
  185.   if (!this._indexDate) {
  186.     this._indexDate = 0;
  187.     var iniFile = GF_GetIndexesDir();
  188.     iniFile.append("info.ini");
  189.     if (iniFile.exists()) {
  190.       try {
  191.         var ini = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]
  192.                     .getService(Ci.nsIINIParserFactory).createINIParser(iniFile);
  193.         var dateString = ini.getString("indexes", "date");
  194.         this._indexDate = ISO8601DateUtils.parse(dateString).getTime();
  195.       }
  196.       catch (e) {
  197.         Cu.reportError(e);
  198.       }
  199.     }
  200.   }
  201.   return this._indexDate;
  202. });
  203.  
  204. gfGreasefireService.prototype._fixUrl =
  205. function gfGreasefireService__fixUrl(aURL)
  206. {
  207.   var urlSpec = aURL.spec;
  208.  
  209.   // XXX performance breaks down on really long URLs, so trim to 50 chars
  210.   urlSpec = urlSpec.substring(0, 50);
  211.   return urlSpec;
  212. }
  213.  
  214. gfGreasefireService.prototype._getScriptInfos =
  215. function gfGreasefireService__getSctiptInfos(matches)
  216. {
  217.   var ids = [];
  218.   for (var id in matches) {
  219.     ids.push(id);
  220.   }
  221.  
  222.   var infos = {};
  223.   var sql = "select id, name, installs, updated from scripts where id in (";
  224.   sql += ids.join(",");
  225.   sql += ");";
  226.  
  227.   var stmt = this._conn.createStatement(sql);
  228.   while (stmt.executeStep()) {
  229.     infos[stmt.getInt32(0)] = {
  230.       name:     stmt.getString(1),
  231.       installs: stmt.getInt32(2),
  232.       updated:  stmt.getInt64(3)
  233.     }
  234.   }
  235.   stmt.finalize();
  236.   return infos;
  237. }
  238.  
  239. gfGreasefireService.prototype._rankMatches =
  240. function gfGreasefireService__rankMatches(matches, infos)
  241. {
  242.   var ranks = [];
  243.   var updatedMin = null;
  244.   var updatedMax = null;
  245.   var installsMin = null;
  246.   var installsMax = null;
  247.   var matchMin = null;
  248.   var matchMax = null;
  249.  
  250.   for (var id in matches) {
  251.     var info = infos[id];
  252.     var match = matches[id];
  253.     if (updatedMin == null || info.updated < updatedMin) {
  254.       updatedMin = info.updated;
  255.     }
  256.     if (updatedMax == null || info.updated > updatedMax) {
  257.       updatedMax = info.updated;
  258.     }
  259.  
  260.     if (installsMin == null || info.installs < installsMin) {
  261.       installsMin = info.installs;
  262.     }
  263.     if (installsMax == null || info.installs > installsMax) {
  264.       installsMax = info.installs;
  265.     }
  266.  
  267.     if (matchMin == null || match < matchMin) {
  268.       matchMin = match;
  269.     }
  270.     if (matchMax == null || match > matchMax) {
  271.       matchMax = match;
  272.     }
  273.   }
  274.  
  275.   var updatedRange = updatedMax - updatedMin;
  276.   var installsRange = installsMax - installsMin;
  277.   var matchRange = matchMax - matchMin;
  278.  
  279.   for (var id in matches) {
  280.     var info = infos[id];
  281.     var matchCount = matches[id];
  282.  
  283.     var updated = updatedRange > 0 ?
  284.                   (info.updated - updatedMin) / updatedRange : 1;
  285.     var installs = installsRange > 0 ?
  286.                    (info.installs - installsMin) / installsRange : 1;
  287.     var match = matchRange > 0 ?
  288.                 (matchCount - matchMin) / matchRange : 1;
  289.  
  290.     ranks[id] = (updated * .5) + (installs * .25) + (match * .25);
  291.   }
  292.  
  293.   return ranks;
  294. }
  295.  
  296. // nsIObserver
  297.  
  298. gfGreasefireService.prototype.observe =
  299. function gfGreasefireService_observe(aSubject, aTopic, aData)
  300. {
  301.   if (aTopic == NS_PROFILE_STARTUP_OBSERVER_ID) {
  302.     this.startup();
  303.   }
  304.   else if (aTopic == NS_PROFILE_SHUTDOWN_OBSERVER_ID) {
  305.     this.shutdown();
  306.     var obs = Cc["@mozilla.org/observer-service;1"]
  307.                 .getService(Ci.nsIObserverService);
  308.     obs.removeObserver(this, NS_PROFILE_STARTUP_OBSERVER_ID);
  309.     obs.removeObserver(this, NS_PROFILE_SHUTDOWN_OBSERVER_ID);
  310.   }
  311. }
  312.  
  313. function gfSearchResult(aScriptId, aInfo, aMatch, aRank)
  314. {
  315.   this._scriptId = aScriptId;
  316.   this._info = aInfo;
  317.   this._match = aMatch;
  318.   this._rank = aRank;
  319. }
  320.  
  321. gfSearchResult.prototype = {
  322.   get scriptId() {
  323.     return this._scriptId;
  324.   },
  325.   get match() {
  326.     return this._match;
  327.   },
  328.   get name() {
  329.     return this._info.name;
  330.   },
  331.   get installs() {
  332.     return this._info.installs;
  333.   },
  334.   get updated() {
  335.     return this._info.updated;
  336.   },
  337.   get rank() {
  338.     return this._rank;
  339.   }
  340.  
  341. }
  342.  
  343. function gfIndexReader(aFile)
  344. {
  345.   this._fis = Cc["@mozilla.org/network/file-input-stream;1"]
  346.                 .createInstance(Ci.nsIFileInputStream);
  347.   this._fis.init(aFile, -1, 0, 0);
  348.   var buffer = Cc["@mozilla.org/network/buffered-input-stream;1"]
  349.                  .createInstance(Ci.nsIBufferedInputStream);
  350.   buffer.init(this._fis, 4096);
  351.   this._seekable = buffer.QueryInterface(Ci.nsISeekableStream);
  352.  
  353.   this._bis = Cc["@mozilla.org/binaryinputstream;1"]
  354.                 .createInstance(Ci.nsIBinaryInputStream);
  355.   this._bis.setInputStream(buffer);
  356.  
  357.   this._cache = [];
  358. }
  359.  
  360. gfIndexReader.prototype = {
  361.  
  362.   close: function() {
  363.     this._fis.close();
  364.   },
  365.  
  366.   search: function(aUrl, aMatches, aFirstOnly, aExcludes) {
  367.     this._search(aUrl, 0, 0, 0, aMatches, aFirstOnly, aExcludes);
  368.   },
  369.  
  370.   _search: function(aUrl, aIndexPos, aStringPos, aMatchedCount, aMatches, aFirstOnly, aExcludes) {
  371.  
  372.     var o = this._cache[aIndexPos];
  373.     if (!o) {
  374.       o = {};
  375.       this._cache[aIndexPos] = o;
  376.  
  377.       this._seekable.seek(Ci.nsISeekableStream.NS_SEEK_SET, aIndexPos);
  378.  
  379.       var idsLength = this._bis.read64();
  380.       if (idsLength > 0) {
  381.         var ids = [];
  382.         for (var i = 0; i < idsLength; i++) {
  383.           ids.push(this._bis.read64());
  384.         }
  385.         o["ids"] = ids;
  386.       }
  387.  
  388.       var childrenLength = this._bis.read16();
  389.       for (var i = 0; i < childrenLength; i++) {
  390.         var c = String.fromCharCode(this._bis.read16());
  391.         var pos = this._bis.read64();
  392.         o[c] = pos;
  393.       }
  394.     }
  395.  
  396.     var ids = o["ids"];
  397.     if (ids) {
  398.       for (var i = 0; i < ids.length; i++) {
  399.         var id = ids[i];
  400.         var matchedCount = aMatches[id];
  401.         if (!matchedCount || (matchedCount && aMatchedCount > matchedCount)) {
  402.           if (!aExcludes || (aExcludes && !(id in aExcludes))) {
  403.             aMatches[id] = aMatchedCount;
  404.  
  405.             if (aFirstOnly) {
  406.               return false;
  407.             }
  408.           }
  409.         }
  410.       }
  411.     }
  412.  
  413.     var wildPos = o["*"];
  414.     if (wildPos) {
  415.       for (var i = 0; i < aUrl.length; i++) {
  416.         var cont = this._search(aUrl,
  417.                                 wildPos,
  418.                                 i,
  419.                                 aMatchedCount,
  420.                                 aMatches,
  421.                                 aFirstOnly,
  422.                                 aExcludes);
  423.  
  424.         if(!cont) {
  425.           return false;
  426.         }
  427.  
  428.       }
  429.     }
  430.  
  431.     if (aStringPos > aUrl.length - 1) {
  432.       return true;
  433.     }
  434.  
  435.     var nextPos = o[aUrl.charAt(aStringPos)];
  436.     if (nextPos) {
  437.       var cont = this._search(aUrl,
  438.                               nextPos,
  439.                               aStringPos + 1,
  440.                               aMatchedCount + 1,
  441.                               aMatches,
  442.                               aFirstOnly,
  443.                               aExcludes);
  444.  
  445.       if(!cont) {
  446.         return false;
  447.       }
  448.  
  449.     }
  450.  
  451.     var tldPos = o[" "];
  452.     if (tldPos) {
  453.       // Consume all characters up to /
  454.       var slashPos = aUrl.indexOf("/", aStringPos + 1);
  455.       if (slashPos >= 0) {
  456.         var cont = this._search(aUrl,
  457.                                 tldPos,
  458.                                 slashPos,
  459.                                 aMatchedCount + 1,
  460.                                 aMatches,
  461.                                 aFirstOnly,
  462.                                 aExcludes);
  463.  
  464.         if(!cont) {
  465.           return false;
  466.         }
  467.  
  468.       }
  469.  
  470.     }
  471.  
  472.     return true;
  473.   }
  474. }
  475.  
  476. /*
  477.  * ***** BEGIN LICENSE BLOCK *****
  478.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  479.  *
  480.  * The contents of this file are subject to the Mozilla Public License Version
  481.  * 1.1 (the "License"); you may not use this file except in compliance with
  482.  * the License. You may obtain a copy of the License at
  483.  * http://www.mozilla.org/MPL/
  484.  *
  485.  * Software distributed under the License is distributed on an "AS IS" basis,
  486.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  487.  * for the specific language governing rights and limitations under the
  488.  * License.
  489.  *
  490.  * The Original Code is Mozilla code.
  491.  *
  492.  * The Initial Developer of the Original Code is
  493.  * Netscape Communications Corporation.
  494.  * Portions created by the Initial Developer are Copyright (C) 2004
  495.  * the Initial Developer. All Rights Reserved.
  496.  *
  497.  * Contributor(s):
  498.  *    Alex Fritze <alex@croczilla.com> (original author)
  499.  *    Nickolay Ponomarev <asqueella@gmail.com>
  500.  *
  501.  * Alternatively, the contents of this file may be used under the terms of
  502.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  503.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  504.  * in which case the provisions of the GPL or the LGPL are applicable instead
  505.  * of those above. If you wish to allow use of your version of this file only
  506.  * under the terms of either the GPL or the LGPL, and not to allow others to
  507.  * use your version of this file under the terms of the MPL, indicate your
  508.  * decision by deleting the provisions above and replace them with the notice
  509.  * and other provisions required by the GPL or the LGPL. If you do not delete
  510.  * the provisions above, a recipient may use your version of this file under
  511.  * the terms of any one of the MPL, the GPL or the LGPL.
  512.  *
  513.  * ***** END LICENSE BLOCK ***** */
  514.  
  515. /**
  516.  * Utilities for JavaScript components loaded by the JS component
  517.  * loader.
  518.  *
  519.  * Import into a JS component using
  520.  * 'Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");'
  521.  *
  522.  * Exposing a JS 'class' as a component using these utility methods consists
  523.  * of several steps:
  524.  * 0. Import XPCOMUtils, as described above.
  525.  * 1. Declare the 'class' (or multiple classes) implementing the component(s):
  526.  *  function MyComponent() {
  527.  *    // constructor
  528.  *  }
  529.  *  MyComponent.prototype = {
  530.  *    // properties required for XPCOM registration:
  531.  *    classDescription: "unique text description",
  532.  *    classID:          Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"),
  533.  *    contractID:       "@example.com/xxx;1",
  534.  *
  535.  *    // [optional] custom factory (an object implementing nsIFactory). If not
  536.  *    // provided, the default factory is used, which returns
  537.  *    // |(new MyComponent()).QueryInterface(iid)| in its createInstance().
  538.  *    _xpcom_factory: { ... },
  539.  *
  540.  *    // [optional] an array of categories to register this component in.
  541.  *    _xpcom_categories: [{
  542.  *      // Each object in the array specifies the parameters to pass to
  543.  *      // nsICategoryManager.addCategoryEntry(). 'true' is passed for
  544.  *      // both aPersist and aReplace params.
  545.  *      category: "some-category",
  546.  *      // optional, defaults to the object's classDescription
  547.  *      entry: "entry name",
  548.  *      // optional, defaults to the object's contractID (unless
  549.  *      // 'service' is specified)
  550.  *      value: "...",
  551.  *      // optional, defaults to false. When set to true, and only if 'value'
  552.  *      // is not specified, the concatenation of the string "service," and the
  553.  *      // object's contractID is passed as aValue parameter of addCategoryEntry.
  554.  *      service: true
  555.  *    }],
  556.  *
  557.  *    // QueryInterface implementation, e.g. using the generateQI helper
  558.  *    QueryInterface: XPCOMUtils.generateQI(
  559.  *      [Components.interfaces.nsIObserver,
  560.  *       Components.interfaces.nsIMyInterface]),
  561.  *
  562.  *    // ...component implementation...
  563.  *  };
  564.  *
  565.  * 2. Create an array of component constructors (like the one
  566.  * created in step 1):
  567.  *  var components = [MyComponent];
  568.  *
  569.  * 3. Define the NSGetModule entry point:
  570.  *  function NSGetModule(compMgr, fileSpec) {
  571.  *    // components is the array created in step 2.
  572.  *    return XPCOMUtils.generateModule(components);
  573.  *  }
  574.  */
  575.  
  576. var XPCOMUtils = {
  577.   /**
  578.    * Generate a QueryInterface implementation. The returned function must be
  579.    * assigned to the 'QueryInterface' property of a JS object. When invoked on
  580.    * that object, it checks if the given iid is listed in the |interfaces|
  581.    * param, and if it is, returns |this| (the object it was called on).
  582.    */
  583.   generateQI: function(interfaces) {
  584.     return makeQI([i.name for each(i in interfaces)]);
  585.   },
  586.  
  587.   /**
  588.    * Generate the NSGetModule function (along with the module definition).
  589.    * See the parameters to generateModule.
  590.    */
  591.   generateNSGetModule: function(componentsArray, postRegister, preUnregister) {
  592.     return function NSGetModule(compMgr, fileSpec) {
  593.       return XPCOMUtils.generateModule(componentsArray,
  594.                                        postRegister,
  595.                                        preUnregister);
  596.     }
  597.   },
  598.  
  599.   /**
  600.    * Generate a module implementation.
  601.    *
  602.    * @param componentsArray  Array of component constructors. See the comment
  603.    *                         at the top of this file for details.
  604.    * @param postRegister  optional post-registration function with
  605.    *                      signature 'postRegister(nsIComponentManager,
  606.    *                                              nsIFile, componentsArray)'
  607.    * @param preUnregister optional pre-unregistration function with
  608.    *                      signature 'preUnregister(nsIComponentManager,
  609.    *                                               nsIFile, componentsArray)'
  610.    */
  611.   generateModule: function(componentsArray, postRegister, preUnregister) {
  612.     let classes = [];
  613.     for each (let component in componentsArray) {
  614.       classes.push({
  615.         cid:          component.prototype.classID,
  616.         className:    component.prototype.classDescription,
  617.         contractID:   component.prototype.contractID,
  618.         factory:      this._getFactory(component),
  619.         categories:   component.prototype._xpcom_categories
  620.       });
  621.     }
  622.  
  623.     return { // nsIModule impl.
  624.       getClassObject: function(compMgr, cid, iid) {
  625.         // We only support nsIFactory queries, not nsIClassInfo
  626.         if (!iid.equals(Ci.nsIFactory))
  627.           throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  628.  
  629.         for each (let classDesc in classes) {
  630.           if (classDesc.cid.equals(cid))
  631.             return classDesc.factory;
  632.         }
  633.  
  634.         throw Cr.NS_ERROR_FACTORY_NOT_REGISTERED;
  635.       },
  636.  
  637.       registerSelf: function(compMgr, fileSpec, location, type) {
  638.         var componentCount = 0;
  639.         debug("*** registering " + fileSpec.leafName + ": [ ");
  640.         compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  641.  
  642.         for each (let classDesc in classes) {
  643.           debug((componentCount++ ? ", " : "") + classDesc.className);
  644.           compMgr.registerFactoryLocation(classDesc.cid,
  645.                                           classDesc.className,
  646.                                           classDesc.contractID,
  647.                                           fileSpec,
  648.                                           location,
  649.                                           type);
  650.           if (classDesc.categories) {
  651.             let catMan = XPCOMUtils.categoryManager;
  652.             for each (let cat in classDesc.categories) {
  653.               let defaultValue = (cat.service ? "service," : "") +
  654.                                  classDesc.contractID;
  655.               catMan.addCategoryEntry(cat.category,
  656.                                       cat.entry || classDesc.className,
  657.                                       cat.value || defaultValue,
  658.                                       true, true);
  659.             }
  660.           }
  661.         }
  662.  
  663.         if (postRegister)
  664.           postRegister(compMgr, fileSpec, componentsArray);
  665.         debug(" ]\n");
  666.       },
  667.  
  668.       unregisterSelf: function(compMgr, fileSpec, location) {
  669.         var componentCount = 0;
  670.         debug("*** unregistering " + fileSpec.leafName + ": [ ");
  671.         compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  672.         if (preUnregister)
  673.           preUnregister(compMgr, fileSpec, componentsArray);
  674.  
  675.         for each (let classDesc in classes) {
  676.           debug((componentCount++ ? ", " : "") + classDesc.className);
  677.           if (classDesc.categories) {
  678.             let catMan = XPCOMUtils.categoryManager;
  679.             for each (let cat in classDesc.categories) {
  680.               catMan.deleteCategoryEntry(cat.category,
  681.                                          cat.entry || classDesc.className,
  682.                                          true);
  683.             }
  684.           }
  685.           compMgr.unregisterFactoryLocation(classDesc.cid, fileSpec);
  686.         }
  687.         debug(" ]\n");
  688.       },
  689.  
  690.       canUnload: function(compMgr) {
  691.         return true;
  692.       }
  693.     };
  694.   },
  695.  
  696.   /**
  697.    * Convenience access to category manager
  698.    */
  699.   get categoryManager() {
  700.     return Components.classes["@mozilla.org/categorymanager;1"]
  701.            .getService(Ci.nsICategoryManager);
  702.   },
  703.  
  704.   /**
  705.    * Returns an nsIFactory for |component|.
  706.    */
  707.   _getFactory: function(component) {
  708.     var factory = component.prototype._xpcom_factory;
  709.     if (!factory) {
  710.       factory = {
  711.         createInstance: function(outer, iid) {
  712.           if (outer)
  713.             throw Cr.NS_ERROR_NO_AGGREGATION;
  714.           return (new component()).QueryInterface(iid);
  715.         }
  716.       }
  717.     }
  718.     return factory;
  719.   }
  720. };
  721.  
  722. /**
  723.  * Helper for XPCOMUtils.generateQI to avoid leaks - see bug 381651#c1
  724.  */
  725. function makeQI(interfaceNames) {
  726.   return function XPCOMUtils_QueryInterface(iid) {
  727.     if (iid.equals(Ci.nsISupports))
  728.       return this;
  729.     for each(let interfaceName in interfaceNames) {
  730.       if (Ci[interfaceName].equals(iid))
  731.         return this;
  732.     }
  733.  
  734.     throw Cr.NS_ERROR_NO_INTERFACE;
  735.   };
  736. }
  737.  
  738. //@line 477 "/home/steve/dev/mozilla/1.8/mozilla/extensions/greasefire/src/gfGreasefireService.js"
  739.  
  740. gfGreasefireService.prototype.QueryInterface =
  741.   XPCOMUtils.generateQI([Ci.gfIGreasefireService,
  742.                          Ci.nsIObserver]);
  743.  
  744. gfSearchResult.prototype.QueryInterface =
  745.   XPCOMUtils.generateQI([Ci.gfISearchResult]);
  746.  
  747. var NSGetModule = XPCOMUtils.generateNSGetModule(
  748.   [
  749.     gfGreasefireService
  750.   ],
  751.   function(aCompMgr, aFileSpec, aLocation) {
  752.     XPCOMUtils.categoryManager.addCategoryEntry(
  753.       "app-startup",
  754.       gfGreasefireService.prototype.classDescription,
  755.       "service," + gfGreasefireService.prototype.contractID,
  756.       true,
  757.       true);
  758.   }
  759. );
  760.